package com.ssy.lingxi.workflow.serviceimpl;

import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.workflow.entity.ExternalTaskDefinition;
import com.ssy.lingxi.workflow.entity.InternalTaskDefinition;
import com.ssy.lingxi.workflow.model.bo.KeyValueBO;
import com.ssy.lingxi.workflow.model.bo.TaskPropertyBO;
import com.ssy.lingxi.workflow.model.constant.ExternalTaskRoleTypeEnum;
import com.ssy.lingxi.workflow.model.constant.ModelConstant;
import com.ssy.lingxi.workflow.model.constant.ProcessTaskTypeEnum;
import com.ssy.lingxi.workflow.model.constant.ProcessTypeEnum;
import com.ssy.lingxi.workflow.service.IExternalTaskDaoService;
import com.ssy.lingxi.workflow.service.IProcessDeployService;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.model.bpmn.Bpmn;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.Process;
import org.camunda.bpm.model.bpmn.instance.*;
import org.camunda.bpm.model.bpmn.instance.camunda.CamundaProperties;
import org.camunda.bpm.model.bpmn.instance.camunda.CamundaProperty;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 流程图解析相关接口实现类
 * @author 万宁
 * @version 2.0.0
 * @date 2021-04-21
 */
@Service
public class ProcessDeployServiceImpl implements IProcessDeployService {
    @Resource
    private RepositoryService repositoryService;

    @Resource
    private ResourceLoader resourceLoader;

    @Resource
    private IExternalTaskDaoService externalTaskDaoService;

    /**
     * 获取Resource目录下，*.bpmn文件中定义的processKey
     *
     * @return processKey列表
     */
    @Override
    public List<String> getProcessKeyInBpmnFiles() {
        try {
            List<String> processKeyList = new ArrayList<>();
            String pattern = "classpath*:*.bpmn";
            org.springframework.core.io.Resource[] reList = ResourcePatternUtils.getResourcePatternResolver(resourceLoader).getResources(pattern);
            for (org.springframework.core.io.Resource resource1 : reList) {
                BpmnModelInstance model = Bpmn.readModelFromStream(resource1.getInputStream());
                Collection<Process> processes = model.getModelElementsByType(Process.class);
                processes.forEach(process -> processKeyList.add(process.getId()));
            }

            return processKeyList;
        } catch (Exception e) {
            e.printStackTrace();
            return new ArrayList<>();
        }
    }

    /**
     * 检查Resource目录下所有*.bpmn文件中的ProcessKey是否有重复
     *
     * @return 错误信息
     */
    @Override
    public String checkDuplicateProcessKey() {
        List<String> keyList = getProcessKeyInBpmnFiles();
        Map<String, Long> map = keyList.stream()
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

        List<String> result = map.entrySet().stream().filter(entry -> entry.getValue() > 1).map(Map.Entry::getKey).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(result)) {
            return "Resource目录下bpmn文件中的ProcessKey：" + String.join(",", result) + " 定义重复";
        }

        return "";
    }

    /**
     * 删除已经部署，但在Resource目录下没有对应 .bpmn文件的流程
     */
    @Override
    public void deleteDeploymentWithoutBpmnFile() {
        List<String> processKeyList = getProcessKeyInBpmnFiles();
        List<ProcessDefinition> processDefinitionList = repositoryService.createProcessDefinitionQuery().list();
        for (ProcessDefinition processDefinition : processDefinitionList) {
            if (!processKeyList.contains(processDefinition.getKey())) {
                System.out.println("删除流程 " + processDefinition.getName() + " => " + processDefinition.getId());
                repositoryService.deleteDeployment(processDefinition.getDeploymentId(), true);
            }
        }
    }

    /**
     * 检查流程图的定义
     *
     * @param processDefinition 流程定义
     * @param model             Bpmn模型
     */
    @Override
    public String checkProcessModel(ProcessDefinition processDefinition, BpmnModelInstance model) {
        try {
            String processKey = processDefinition.getKey();
            String processName = processDefinition.getName();
            String processId = processDefinition.getId();
            if (!StringUtils.hasText(processKey)) {
                return "流程：" + (StringUtils.hasText(processName) ? processName : processId) + "  未定义ProcessKey";
            }

            if (!StringUtils.hasLength(processName)) {
                return "流程：" + processKey + " 未定义流程名称";
            }

            String msgPrefix = "流程：".concat(processName).concat(" => ");

            //Step 1: 检查流程类型
            //规则：
            // 1) 如果有SubProgress的，为外部流程+内部流程的复杂流程
            // 2) 如果ProcessKey以“_ext”结尾的，认为是外部流程，但不能有SubProgress
            // 3) 其他认为是内部流程
            String msg = checkProcessType(processKey, model);
            if (StringUtils.hasLength(msg)) {
                return msgPrefix.concat(msg);
            }

            //Step 2: 检查StartEvent
            //规则：
            // 1). StartEvent必须定义status
            // 2). StartEvent只能有一条外接连接线
            // 3). StartEvent的下一个节点可以是UserTask、SubProgress，但不能是ExclusiveGateway
            StartEvent startEvent = model.getModelElementsByType(StartEvent.class).stream().filter(st -> st.getParentElement() != null && "process".equals(st.getParentElement().getDomElement().getLocalName())).findFirst().orElse(null);
            if (startEvent == null) {
                return msgPrefix.concat(" 未定义StartEvent");
            }

            msg = checkStartEvent(startEvent);
            if (StringUtils.hasLength(msg)) {
                return msgPrefix.concat(msg);
            }


            //Step 3: 检查所有的节点类型（流程的节点的基类为FlowNode，不包括线条）
            //规则：
            // 1). 只允许使用StartEvent、EndEvent、SubProgress、UserTask、ExclusiveGateway等5中类型的节点
            // 2). 如果在Extensions中定义了属性，则属性名称忽略大小写后不能相同
            // 3). 如果在Extensions中定义了属性，则属性值不能为空，如果为空，Camunda会转换为Null
            msg = checkAllNodeType(model.getModelElementsByType(FlowNode.class));
            if (StringUtils.hasLength(msg)) {
                return msgPrefix.concat(msg);
            }

            //Step 4: 检查Gateway
            //规则：
            // 1). 只允许一条Incoming连接线，必须有两条或以上的Outgoing连接线
            // 2). 每条Outgoing必须定义ConditionExpression（跳转条件），且必须符合格式 “#{agree == ?}”，跳转条件值必须大于等于0
            // 3). 每条Outgoing必须定义status
            msg = checkExclusiveGateway(model);
            if (StringUtils.hasLength(msg)) {
                return msgPrefix.concat(msg);
            }


            //Step 5: 检查UserTask
            //规则：
            // 1). 每个UserTask必须定义名称（不能重复）、step、status
            // 2). 每个UserTask只能有一条Outgoing连接线
            // 3). 如果UserTask的下一个节点（通过getSucceedingNodes()获得）是ExclusiveGateway，则UserTask的status有多个，从ExclusiveGateway的Outgoing中获得
            msg = checkUserTasks(model);
            if (StringUtils.hasLength(msg)) {
                return msgPrefix.concat(msg);
            }

            //Step 6:检查子流程SubProgress
            //规则：
            // 1) 如果有则进行检查
            // 2) 每个SubProgress必须定义step、status
            // 3) 只能有一条Incoming连接线，一条Outgoing连接线
            // 4) 子流程的下一个节点不能是网关
            // 5) 子流程里不能嵌套子流程
            msg = checkSubProgresses(model);
            if (StringUtils.hasLength(msg)) {
                return msgPrefix.concat(msg);
            }

            //Step 6: 检查所有的Step定义
            //规则：
            // 1). Step要大于0
            // 2). 如果有SubProgress，则SubProgress作为外部流程的节点
            msg = checkStepProperty(model);
            if (StringUtils.hasLength(msg)) {
                return msgPrefix.concat(msg);
            }

            return "";

        } catch (Exception e) {
            e.printStackTrace();
            return "流程图校验代码错误 ：" + e.getMessage();
        }
    }

    /**
     * 将流程定义，转换为自定义的数据库表进行存储
     *
     * @param processDefinition 流程定义
     * @param model             Bpmn模型
     */
    @Override
    public void deployProcess(ProcessDefinition processDefinition, BpmnModelInstance model) {
        String processKey = processDefinition.getKey();
        //没有子流程的，是简单流程
        if(CollectionUtils.isEmpty(model.getModelElementsByType(SubProcess.class))) {
            //以“_ext”结尾的为外部流程
            if (processKey.toLowerCase().endsWith(ModelConstant.EXTERNAL_PROCESS_KEY_SUFFIX)) {
                deployExternalProcess(processDefinition, model);
            } else {
                deployInternalProcess(processDefinition, model);
            }
        } else {
            deployComplexProcess(processDefinition, model);
        }
    }

    /**
     * 解析简单流程
     *
     * @param processDefinition 流程定义
     * @param model             Bpmn模型
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public void deployInternalProcess(ProcessDefinition processDefinition, BpmnModelInstance model) {
        String processKey = processDefinition.getKey();
        String processName = processDefinition.getName();

        //删除原有的数据：这里由于设置了Jpa CascadeType = Persist + Remove，所以删除外部流程的时候也会删除其关联的内部流程
        externalTaskDaoService.deleteByProcessKey(processKey);

        //批量保存
        List<ExternalTaskDefinition> processList = new ArrayList<>();

        //Step 1: 将StartEvent作为特殊的外部Task,其Step为0
        StartEvent startEvent = model.getModelElementsByType(StartEvent.class).stream().filter(st -> st.getParentElement() != null && "process".equals(st.getParentElement().getDomElement().getLocalName())).findFirst().orElse(null);
        if (startEvent != null) {
            ExternalTaskDefinition externalTaskDefinition = new ExternalTaskDefinition();
            externalTaskDefinition.setProcessKey(processKey);
            externalTaskDefinition.setProcessName(processName);
            externalTaskDefinition.setProcessType(ProcessTypeEnum.INTERNAL_PROCESS.getCode());

            Map<String, String> propertyMap = getPropertyMap(startEvent.getExtensionElements());
            externalTaskDefinition.setRoleType(ExternalTaskRoleTypeEnum.DEFAULT.getCode());
            externalTaskDefinition.setStatus(getIntValueThenRemoveKey(propertyMap, ModelConstant.STATUS_PROPERTY_NAME, 0));
            externalTaskDefinition.setStatusDesc(getStrValueThenRemoveKey(propertyMap, ModelConstant.STATUS_DESCRIPTION_PROPERTY_NAME));
            externalTaskDefinition.setTaskName(StringUtils.hasText(startEvent.getName()) ? startEvent.getName().trim() : "流程启动");
            externalTaskDefinition.setTaskStep(0);
            externalTaskDefinition.setTaskType(ProcessTaskTypeEnum.START_EVENT.getCode());
            externalTaskDefinition.setAgree(ModelConstant.NO_AGREE);
            externalTaskDefinition.setOther(convertMapToKeyValueList(propertyMap));
            externalTaskDefinition.setSubTasks(new HashSet<>());
            processList.add(externalTaskDefinition);
        }

        //Step 2: 创建一个虚拟的外部流程，其subTasks为内部流程
        ExternalTaskDefinition externalTaskDefinition = new ExternalTaskDefinition();
        externalTaskDefinition.setProcessKey(processKey);
        externalTaskDefinition.setProcessName(processName);
        externalTaskDefinition.setProcessType(ProcessTypeEnum.INTERNAL_PROCESS.getCode());
        externalTaskDefinition.setRoleType(ExternalTaskRoleTypeEnum.DEFAULT.getCode());
        externalTaskDefinition.setStatus(0);
        externalTaskDefinition.setStatusDesc("");
        externalTaskDefinition.setTaskName("虚拟任务");
        externalTaskDefinition.setTaskStep(0);
        externalTaskDefinition.setTaskType(ProcessTaskTypeEnum.VIRTUAL_TASK.getCode());
        externalTaskDefinition.setAgree(ModelConstant.NO_AGREE);
        externalTaskDefinition.setOther(new ArrayList<>());

        //Step 3: 内部UserTask、排序
        Set<InternalTaskDefinition> set = model.getModelElementsByType(UserTask.class).stream().flatMap(userTask -> getUserTaskProperty(userTask).stream().map(propertyBO -> {
            InternalTaskDefinition internalTaskDefinition = new InternalTaskDefinition();
            internalTaskDefinition.setProcessKey(processKey);
            internalTaskDefinition.setProcessName(processName);
            internalTaskDefinition.setTaskStep(propertyBO.getTaskStep());
            internalTaskDefinition.setTaskName(propertyBO.getTaskName());
            internalTaskDefinition.setAgree(propertyBO.getAgree());
            internalTaskDefinition.setStatus(propertyBO.getStatus());
            internalTaskDefinition.setStatusDesc(propertyBO.getDesc());
            internalTaskDefinition.setTaskType(ProcessTaskTypeEnum.USER_TASK.getCode());
            internalTaskDefinition.setUrl(propertyBO.getUrl());
            internalTaskDefinition.setOther(convertMapToKeyValueList(propertyBO.getOther()));
            internalTaskDefinition.setExtTask(externalTaskDefinition);
            return internalTaskDefinition;
        })).sorted(Comparator.comparingInt(InternalTaskDefinition::getTaskStep)).collect(Collectors.toCollection(LinkedHashSet::new));
        externalTaskDefinition.setSubTasks(set);
        processList.add(externalTaskDefinition);

        externalTaskDaoService.saveAll(processKey, processList);
    }

    /**
     * 解析外部流程
     *
     * @param processDefinition 流程定义
     * @param model             Bpmn模型
     */
    @Override
    public void deployExternalProcess(ProcessDefinition processDefinition, BpmnModelInstance model) {
        String processKey = processDefinition.getKey();
        String processName = processDefinition.getName();

        //删除原有的数据：这里由于设置了Jpa CascadeType = Persist + Remove，所以删除外部流程的时候也会删除其关联的内部流程
        externalTaskDaoService.deleteByProcessKey(processKey);

        //批量保存
        List<ExternalTaskDefinition> processList = new ArrayList<>();

        //Step 1: 将StartEvent作为特殊的Task,其Step为0
        StartEvent startEvent = model.getModelElementsByType(StartEvent.class).stream().filter(st -> st.getParentElement() != null && "process".equals(st.getParentElement().getDomElement().getLocalName())).findFirst().orElse(null);
        if (startEvent != null) {
            ExternalTaskDefinition externalTaskDefinition = new ExternalTaskDefinition();
            externalTaskDefinition.setProcessKey(processKey);
            externalTaskDefinition.setProcessName(processName);
            externalTaskDefinition.setProcessType(ProcessTypeEnum.EXTERNAL_PROCESS.getCode());

            Map<String, String> propertyMap = getPropertyMap(startEvent.getExtensionElements());
            externalTaskDefinition.setRoleType(ExternalTaskRoleTypeEnum.DEFAULT.getCode());
            externalTaskDefinition.setStatus(getIntValueThenRemoveKey(propertyMap, ModelConstant.STATUS_PROPERTY_NAME, 0));
            externalTaskDefinition.setStatusDesc(getStrValueThenRemoveKey(propertyMap, ModelConstant.STATUS_DESCRIPTION_PROPERTY_NAME));
            externalTaskDefinition.setTaskName(StringUtils.hasText(startEvent.getName()) ? startEvent.getName().trim() : "流程启动");
            externalTaskDefinition.setTaskStep(0);
            externalTaskDefinition.setTaskType(ProcessTaskTypeEnum.START_EVENT.getCode());
            externalTaskDefinition.setAgree(ModelConstant.NO_AGREE);
            externalTaskDefinition.setOther(convertMapToKeyValueList(propertyMap));
            externalTaskDefinition.setSubTasks(new HashSet<>());
            processList.add(externalTaskDefinition);
        }

        //Step 2: 外部UserTask，排序
        processList.addAll(model.getModelElementsByType(UserTask.class).stream().flatMap(userTask -> getUserTaskProperty(userTask).stream().map(propertyBO -> {
            ExternalTaskDefinition externalTaskDefinition = new ExternalTaskDefinition();
            externalTaskDefinition.setProcessKey(processKey);
            externalTaskDefinition.setProcessName(processName);
            externalTaskDefinition.setProcessType(ProcessTypeEnum.EXTERNAL_PROCESS.getCode());
            externalTaskDefinition.setRoleType(propertyBO.getRoleType());
            externalTaskDefinition.setStatus(propertyBO.getStatus());
            externalTaskDefinition.setStatusDesc(propertyBO.getDesc());
            externalTaskDefinition.setTaskStep(propertyBO.getTaskStep());
            externalTaskDefinition.setTaskName(propertyBO.getTaskName());
            externalTaskDefinition.setAgree(propertyBO.getAgree());
            externalTaskDefinition.setTaskType(ProcessTaskTypeEnum.USER_TASK.getCode());
            externalTaskDefinition.setOther(convertMapToKeyValueList(propertyBO.getOther()));
            externalTaskDefinition.setSubTasks(new HashSet<>());
            return externalTaskDefinition;
        })).sorted(Comparator.comparingInt(ExternalTaskDefinition::getTaskStep).thenComparingInt(ExternalTaskDefinition::getAgree)).collect(Collectors.toList()));

        externalTaskDaoService.saveAll(processKey, processList);
    }

    /**
     * 解析复杂流程
     *
     * @param processDefinition 流程定义
     * @param model             Bpmn模型
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deployComplexProcess(ProcessDefinition processDefinition, BpmnModelInstance model) {
        String processKey = processDefinition.getKey();
        String processName = processDefinition.getName();

        //删除原有的数据：这里由于设置了Jpa CascadeType = Remove，所以删除外部流程的时候也会删除其关联的内部流程
        externalTaskDaoService.deleteByProcessKey(processKey);

        //批量保存
        List<ExternalTaskDefinition> processList = new ArrayList<>();

        //Step 1: 将StartEvent作为特殊的Task,其Step为0
        StartEvent startEvent = model.getModelElementsByType(StartEvent.class).stream().filter(st -> st.getParentElement() != null && "process".equals(st.getParentElement().getDomElement().getLocalName())).findFirst().orElse(null);
        if (startEvent != null) {
            ExternalTaskDefinition externalTaskDefinition = new ExternalTaskDefinition();
            externalTaskDefinition.setProcessKey(processKey);
            externalTaskDefinition.setProcessName(processName);
            externalTaskDefinition.setProcessType(ProcessTypeEnum.COMPLEX_PROCESS.getCode());

            Map<String, String> propertyMap = getPropertyMap(startEvent.getExtensionElements());
            externalTaskDefinition.setRoleType(ExternalTaskRoleTypeEnum.DEFAULT.getCode());
            externalTaskDefinition.setStatus(getIntValueThenRemoveKey(propertyMap, ModelConstant.STATUS_PROPERTY_NAME, 0));
            externalTaskDefinition.setStatusDesc(getStrValueThenRemoveKey(propertyMap, ModelConstant.STATUS_DESCRIPTION_PROPERTY_NAME));
            externalTaskDefinition.setTaskName(StringUtils.hasText(startEvent.getName()) ? startEvent.getName().trim() : "流程启动");
            externalTaskDefinition.setTaskStep(0);
            externalTaskDefinition.setTaskType(ProcessTaskTypeEnum.START_EVENT.getCode());
            externalTaskDefinition.setAgree(ModelConstant.NO_AGREE);
            externalTaskDefinition.setOther(convertMapToKeyValueList(propertyMap));
            externalTaskDefinition.setSubTasks(new HashSet<>());
            processList.add(externalTaskDefinition);
        }

        //Step 2: 将子流程解析为外部流程，其内部的流程解析为内部流程
        Collection<SubProcess> subProcesses = model.getModelElementsByType(SubProcess.class);
        subProcesses.forEach(subProcess -> {
            ExternalTaskDefinition externalTaskDefinition = new ExternalTaskDefinition();
            externalTaskDefinition.setProcessKey(processKey);
            externalTaskDefinition.setProcessName(processName);
            externalTaskDefinition.setProcessType(ProcessTypeEnum.COMPLEX_PROCESS.getCode());

            Map<String, String> subProgressMap = getPropertyMap(subProcess.getExtensionElements());
            externalTaskDefinition.setRoleType(getIntValueThenRemoveKey(subProgressMap, ModelConstant.EXTERNAL_PROCESS_ROLE_TYPE_PROPERTY_NAME, ExternalTaskRoleTypeEnum.DEFAULT.getCode()));
            externalTaskDefinition.setStatus(getIntValueThenRemoveKey(subProgressMap, ModelConstant.STATUS_PROPERTY_NAME, 0));
            externalTaskDefinition.setStatusDesc(getStrValueThenRemoveKey(subProgressMap, ModelConstant.STATUS_DESCRIPTION_PROPERTY_NAME));
            externalTaskDefinition.setTaskName(subProcess.getName());
            externalTaskDefinition.setTaskStep(getIntValueThenRemoveKey(subProgressMap, ModelConstant.STEP_PROPERTY_NAME, 0));
            externalTaskDefinition.setTaskType(ProcessTaskTypeEnum.SUB_PROCESS.getCode());
            externalTaskDefinition.setAgree(ModelConstant.NO_AGREE);
            externalTaskDefinition.setOther(convertMapToKeyValueList(subProgressMap));
            externalTaskDefinition.setSubTasks(new HashSet<>());

            //内部UserTask
            Set<InternalTaskDefinition> set = subProcess.getChildElementsByType(UserTask.class).stream().flatMap(userTask -> getUserTaskProperty(userTask).stream().map(propertyBO -> {
                InternalTaskDefinition internalTaskDefinition = new InternalTaskDefinition();
                internalTaskDefinition.setProcessKey(processKey);
                internalTaskDefinition.setProcessName(processName);
                internalTaskDefinition.setTaskStep(propertyBO.getTaskStep());
                internalTaskDefinition.setTaskName(propertyBO.getTaskName());
                internalTaskDefinition.setAgree(propertyBO.getAgree());
                internalTaskDefinition.setStatus(propertyBO.getStatus());
                internalTaskDefinition.setStatusDesc(propertyBO.getDesc());
                internalTaskDefinition.setTaskType(ProcessTaskTypeEnum.USER_TASK.getCode());
                internalTaskDefinition.setUrl(propertyBO.getUrl());
                internalTaskDefinition.setOther(convertMapToKeyValueList(propertyBO.getOther()));
                internalTaskDefinition.setExtTask(externalTaskDefinition);
                return internalTaskDefinition;
            })).collect(Collectors.toSet());
            externalTaskDefinition.setSubTasks(set);
            processList.add(externalTaskDefinition);
        });


        //Step 3: 子流程外的UserTask为外部流程
        List<UserTask> internalTasks = subProcesses.stream().flatMap(p -> p.getChildElementsByType(UserTask.class).stream()).collect(Collectors.toList());
        List<UserTask> externalTasks = model.getModelElementsByType(UserTask.class).stream().filter(task -> internalTasks.stream().noneMatch(t -> t.getId().equals(task.getId()))).collect(Collectors.toList());
        processList.addAll(externalTasks.stream().flatMap(userTask -> getUserTaskProperty(userTask).stream().map(propertyBO -> {
            ExternalTaskDefinition externalTaskDefinition = new ExternalTaskDefinition();
            externalTaskDefinition.setProcessKey(processKey);
            externalTaskDefinition.setProcessName(processName);
            externalTaskDefinition.setProcessType(ProcessTypeEnum.COMPLEX_PROCESS.getCode());
            externalTaskDefinition.setRoleType(propertyBO.getRoleType());
            externalTaskDefinition.setStatus(propertyBO.getStatus());
            externalTaskDefinition.setStatusDesc(propertyBO.getDesc());
            externalTaskDefinition.setTaskName(propertyBO.getTaskName());
            externalTaskDefinition.setTaskStep(propertyBO.getTaskStep());
            externalTaskDefinition.setTaskType(ProcessTaskTypeEnum.USER_TASK.getCode());
            externalTaskDefinition.setAgree(propertyBO.getAgree());
            externalTaskDefinition.setOther(convertMapToKeyValueList(propertyBO.getOther()));
            externalTaskDefinition.setSubTasks(new HashSet<>());
            return externalTaskDefinition;
        })).collect(Collectors.toList()));

        //Step 4: 排序
        List<ExternalTaskDefinition> sortList = processList.stream().sorted(Comparator.comparingInt(ExternalTaskDefinition::getTaskStep)).collect(Collectors.toList());
        sortList.forEach(p -> p.setSubTasks(p.getSubTasks().stream().sorted(Comparator.comparingInt(InternalTaskDefinition::getTaskStep)).collect(Collectors.toCollection(LinkedHashSet::new))));
        externalTaskDaoService.saveAll(processKey, sortList);
    }


    /**
     * 检查流程类型
     *
     * @param processKey 流程Key
     * @param model      流程模型
     * @return 错误信息
     */
    private String checkProcessType(String processKey, BpmnModelInstance model) {
        if (processKey.toLowerCase().endsWith(ModelConstant.EXTERNAL_PROCESS_KEY_SUFFIX)) {
            if (!CollectionUtils.isEmpty(model.getModelElementsByType(SubProcess.class))) {
                return "外部流程不能包含SubProgress";
            }
        }

        return "";
    }

    /**
     * 检查StartEvent
     * <p>Camunda会自动检查“没有StartEvent”和“两个及以上StartEvent”的情况</p>
     *
     * @param startEvent 流程启动节点
     * @return 错误信息
     */
    private String checkStartEvent(StartEvent startEvent) {
        Map<String, String> propertyMap = getPropertyMap(startEvent.getExtensionElements());
        String result = checkPropertyMap(propertyMap, Stream.of("status").collect(Collectors.toList()));
        if (StringUtils.hasLength(result)) {
            return "StartEvent " + result;
        }

        //只能有一条Outgoing连接线
        if(CollectionUtils.isEmpty(startEvent.getOutgoing()) || startEvent.getOutgoing().size() > 1) {
            return "StartEvent 只能有一条外向连接线";
        }

        //检查下一个节点
        FlowNode node =  CollectionUtils.isEmpty(startEvent.getSucceedingNodes().list()) ? null : startEvent.getSucceedingNodes().singleResult();
        if(node != null && !"subProcess".equals(node.getElementType().getTypeName()) && !"userTask".equals(node.getElementType().getTypeName())) {
            return "StartEvent的下一个节点必须是 SubProcess 或 UserTask";
        }

        return "";
    }

    /**
     * 检查除连接线外的所有节点的类型
     * <p>只允许使用StartEvent、EndEvent、SubProgress、UserTask、ExclusiveGateway</p>
     *
     * @param flowNodeList 所有除连接线外的节点
     * @return 错误信息
     */
    private String checkAllNodeType(Collection<FlowNode> flowNodeList) {
        for (FlowNode flowNode : flowNodeList) {
            String typeName = flowNode.getElementType().getTypeName();
            if (!ModelConstant.AVAILABLE_NODE_TYPES.contains(typeName)) {
                return "流程图不允许使用" + typeName + "类型";
            }

            String result = checkExtensionElements(flowNode.getExtensionElements());
            if (StringUtils.hasLength(result)) {
                return (StringUtils.hasLength(flowNode.getName()) ? flowNode.getName() : flowNode.getElementType().getTypeName() + " id:" + flowNode.getId()) + result;
            }
        }


        List<FlowNode> subProcesses = flowNodeList.stream().filter(node -> "subProcess".equals(node.getElementType().getTypeName()) && StringUtils.hasLength(node.getName())).collect(Collectors.toList());
        List<FlowNode> userTasks = flowNodeList.stream().filter(node -> "userTask".equals(node.getElementType().getTypeName()) && StringUtils.hasLength(node.getName())).collect(Collectors.toList());

        if(CollectionUtils.isEmpty(subProcesses)) {
            List<String> nodeNames = userTasks.stream().map(FlowNode::getName).collect(Collectors.toList());
            if(nodeNames.size() != nodeNames.stream().distinct().count()) {
                return "UserTask名称重复";
            }
        } else {
            //外部流程节点名称
            List<String> externalNodeNames = subProcesses.stream().map(FlowNode::getName).collect(Collectors.toList());
            List<String> internalTaskNames = subProcesses.stream().flatMap(p -> p.getChildElementsByType(UserTask.class).stream()).map(UserTask::getName).collect(Collectors.toList());
            if(internalTaskNames.size() != internalTaskNames.stream().distinct().count()) {
                return "内部流程UserTask名称重复";
            }

            List<String> externalTaskNames = userTasks.stream().filter(node -> !internalTaskNames.contains(node.getName())).map(FlowNode::getName).collect(Collectors.toList());
            externalNodeNames.addAll(externalTaskNames);
            if(externalNodeNames.size() != externalNodeNames.stream().distinct().count()) {
                return "外部流程节点名称重复";
            }
        }



        return "";
    }

    /**
     * 检查所有ExclusiveGateway
     *
     * @param model 流程图模型
     * @return 错误信息
     */
    private String checkExclusiveGateway(BpmnModelInstance model) {
        Collection<ExclusiveGateway> gateways = model.getModelElementsByType(ExclusiveGateway.class);
        for (ExclusiveGateway gateway : gateways) {
            String msg = "网关：" + (StringUtils.hasText(gateway.getName()) ? gateway.getName() : gateway.getId());
            if (CollectionUtils.isEmpty(gateway.getOutgoing())) {
                return msg + "没有定义Outgoing连接线";
            }

            if (gateway.getOutgoing().size() < 2) {
                return msg + "必须有两条或两条以上的Outgoing连接线";
            }

            for (SequenceFlow sequenceFlow : gateway.getOutgoing()) {
                //如果是Default Flow，跳过不判断
                if(gateway.getDefault() != null && sequenceFlow.getId().equals(gateway.getDefault().getId())) {
                    System.out.println(sequenceFlow.getName() + " => " + sequenceFlow.getId());
                    continue;
                }

                String result = checkExtensionProperty(sequenceFlow.getExtensionElements(), Stream.of(ModelConstant.STATUS_PROPERTY_NAME).collect(Collectors.toList()));
                if (StringUtils.hasText(result)) {
                    return msg + "的Outgoing连接线" + (StringUtils.hasText(sequenceFlow.getName()) ? sequenceFlow.getName() : sequenceFlow.getId()) + result;
                }

                if(sequenceFlow.getConditionExpression() == null) {
                    return msg + "的Outgoing连接线未定义跳转条件表达式";
                }

                result = checkConditionExpression(sequenceFlow.getConditionExpression().getDomElement().getTextContent());
                if (StringUtils.hasText(result)) {
                    return msg + "的Outgoing连接线" + (StringUtils.hasText(sequenceFlow.getName()) ? sequenceFlow.getName() : sequenceFlow.getId()) + result;
                }
            }
        }

        return "";
    }

    /**
     * 检查所有UserTask
     *
     * @param model 流程图模型
     * @return 错误信息
     */
    private String checkUserTasks(BpmnModelInstance model) {
        Collection<UserTask> userTasks = model.getModelElementsByType(UserTask.class);
        if (CollectionUtils.isEmpty(userTasks)) {
            return "必须定义至少一个UserTask";
        }

        List<String> taskNames = userTasks.stream().filter(task -> StringUtils.hasText(task.getName())).map(FlowElement::getName).collect(Collectors.toList());
        if (taskNames.size() != userTasks.size()) {
            return "包含了未定义名称的UserTask";
        }

        //每个UserTask只能有一条outgoing的线条
        taskNames = userTasks.stream().filter(task -> CollectionUtils.isEmpty(task.getOutgoing()) || task.getOutgoing().size() > 1).map(UserTask::getName).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(taskNames)) {
            return "UserTask: " + String.join(",", taskNames) + " 只能有一条外出的线条，如果有跳转，请画Exclusive网关";
        }

        for (UserTask userTask : userTasks) {
            Map<String, String> propertyMap = findUserTaskProperty(userTask);
            String result = checkPropertyMap(propertyMap, Stream.of(ModelConstant.STEP_PROPERTY_NAME, ModelConstant.STATUS_PROPERTY_NAME).collect(Collectors.toList()), Stream.of(ModelConstant.EXTERNAL_PROCESS_ROLE_TYPE_PROPERTY_NAME).collect(Collectors.toList()));
            if (StringUtils.hasText(result)) {
                return "UserTask：" + userTask.getName() + " " + result;
            }
        }

        return "";
    }

    /**
     * 检查所有SubProgress
     *
     * @param model 流程图模型
     * @return 错误信息
     */
    private String checkSubProgresses(BpmnModelInstance model) {
        Collection<SubProcess> subProcesses = model.getModelElementsByType(SubProcess.class);
        if (CollectionUtils.isEmpty(subProcesses)) {
            return "";
        }

        for (SubProcess subProcess : subProcesses) {
            String name = subProcess.getName();
            if (!StringUtils.hasText(name)) {
                return "子流程：" + subProcess.getId() + " 未定义名称";
            }

            //子流程不能嵌套
            if(!CollectionUtils.isEmpty(subProcess.getChildElementsByType(SubProcess.class))) {
                return "子流程：" + subProcess.getName() + " 不能包含子流程";
            }

            //只能有一条Outgoing连接线
            if(CollectionUtils.isEmpty(subProcess.getOutgoing()) || subProcess.getOutgoing().size() != 1) {
                return "子流程：" + subProcess.getName() + " 必须有且只能有一条Outgoing连接线";
            }

            //检查属性
            Map<String, String> propertyMap = getPropertyMap(subProcess.getExtensionElements());
            String result = checkPropertyMap(propertyMap, Stream.of(ModelConstant.STEP_PROPERTY_NAME, ModelConstant.STATUS_PROPERTY_NAME).collect(Collectors.toList()), Stream.of(ModelConstant.EXTERNAL_PROCESS_ROLE_TYPE_PROPERTY_NAME).collect(Collectors.toList()));
            if (StringUtils.hasLength(result)) {
                return "子流程：" + subProcess.getName() + " " + result;
            }

            //检查StartEvent
            Collection<StartEvent> startEvents = subProcess.getChildElementsByType(StartEvent.class);
            if (CollectionUtils.isEmpty(startEvents)) {
                return "子流程：" + subProcess.getName() + " 没有定义StartEvent";
            }

            List<StartEvent> startEventList = startEvents.stream().filter(st -> st.getParentElement() != null && "process".equals(st.getParentElement().getDomElement().getLocalName())).collect(Collectors.toList());
            if (startEventList.size() > 1) {
                return "子流程：" + subProcess.getName() + " 只能定义一个StartEvent";
            }

            //检查UserTask
            Collection<UserTask> userTasks = subProcess.getChildElementsByType(UserTask.class);
            if (CollectionUtils.isEmpty(userTasks)) {
                return "子流程：" + subProcess.getName() + " 必须至少定义一个UserTask";
            }

            //检查EndEvent
            Collection<EndEvent> endEvents = subProcess.getChildElementsByType(EndEvent.class);
            if (CollectionUtils.isEmpty(endEvents)) {
                return "子流程：" + subProcess.getName() + " 必须至少定义一个EndEvent";
            }
        }

        return "";
    }

    /**
     * 检查SubProcess、UserTask的step、status
     * @param model 流程图模型
     * @return 错误信息
     */
    private String checkStepProperty(BpmnModelInstance model) {
        Collection<UserTask> userTasks = model.getModelElementsByType(UserTask.class);
        Collection<SubProcess> subProcesses = model.getModelElementsByType(SubProcess.class);

        List<Integer> outerStepList = new ArrayList<>();
        List<UserTask> innerTaskList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(subProcesses)) {
            for (SubProcess subProcess : subProcesses) {
                Map<String, String> propertyMap = getPropertyMap(subProcess.getExtensionElements());
                String result = checkPropertyMap(propertyMap, Stream.of(ModelConstant.STEP_PROPERTY_NAME, ModelConstant.STATUS_PROPERTY_NAME).collect(Collectors.toList()));
                if(StringUtils.hasLength(result)) {
                    return "子流程：" + subProcess.getName() + " " + result;
                }

                outerStepList.add(Integer.parseInt(propertyMap.get(ModelConstant.STEP_PROPERTY_NAME)));

                //子流程内部的UserTask
                Collection<UserTask> innerTasks = subProcess.getChildElementsByType(UserTask.class);
                if(CollectionUtils.isEmpty(innerTasks)) {
                    return "子流程：" + subProcess.getName() + " 内部必须有至少一个UserTask";
                }

                List<Integer> innerStepList = new ArrayList<>();
                for (UserTask innerTask : innerTasks) {
                    Map<String, String> innerTaskMap = getPropertyMap(innerTask.getExtensionElements());
                    result = checkPropertyMap(innerTaskMap, Stream.of(ModelConstant.STEP_PROPERTY_NAME).collect(Collectors.toList()));
                    if(StringUtils.hasLength(result)) {
                        return "UserTask：" + innerTask.getName() + " " + result;
                    }

                    int innerStep = Integer.parseInt(innerTaskMap.get(ModelConstant.STEP_PROPERTY_NAME));
                    if(innerStep <= 0) {
                        return "子流程：" + subProcess.getName() + " 内部UserTask:" + innerTask.getName() + " step属性要大于0";
                    }

                    innerStepList.add(innerStep);
                    innerTaskList.add(innerTask);
                }

                if(innerStepList.size() != innerStepList.stream().distinct().count()) {
                    return "子流程：" + subProcess.getName() + " 内部UserTask的step定义重复";
                }
            }
        }

        List<UserTask> outerTasks = userTasks.stream().filter(userTask -> innerTaskList.stream().noneMatch(innerTask -> innerTask.getId().equals(userTask.getId()))).collect(Collectors.toList());
        if(!CollectionUtils.isEmpty(outerTasks)) {
            for (UserTask userTask : outerTasks) {
                Map<String, String> propertyMap = getPropertyMap(userTask.getExtensionElements());
                String result = checkPropertyMap(propertyMap, Stream.of(ModelConstant.STEP_PROPERTY_NAME).collect(Collectors.toList()));
                if(StringUtils.hasLength(result)) {
                    return "UserTask：" + userTask.getName() + " " + result;
                }
                outerStepList.add(Integer.parseInt(propertyMap.get(ModelConstant.STEP_PROPERTY_NAME)));
            }
        }

        if(outerStepList.stream().anyMatch(s -> s <= 0)) {
            return "step要大于0";
        }

        if (outerStepList.size() != outerStepList.stream().distinct().count()) {
            return "step定义重复";
        }

        return "";
    }


    /**
     * 检查Extensions属性是否有重复的key
     *
     * @param extensionElements Extensions属性
     * @return 错误信息
     */
    private String checkExtensionElements(ExtensionElements extensionElements) {
        if (extensionElements == null) {
            return "";
        }

        CamundaProperties camundaProperties = extensionElements.getElementsQuery().filterByType(CamundaProperties.class).singleResult();
        List<String> nameList = camundaProperties.getCamundaProperties().stream().map(p -> p.getCamundaName().toLowerCase()).collect(Collectors.toList());
        if (nameList.size() != nameList.stream().distinct().count()) {
            return "Extensions属性有重复的key";
        }

        List<String> valueEmptyNames = camundaProperties.getCamundaProperties().stream().filter(camundaProperty -> !StringUtils.hasText(camundaProperty.getCamundaValue())).map(CamundaProperty::getCamundaName).collect(Collectors.toList());
        if(!CollectionUtils.isEmpty(valueEmptyNames)) {
            return String.join(",", valueEmptyNames) + "属性值为空";
        }

        return "";
    }


    /**
     * 获取status的值（列表）
     *
     * @param value status的值
     * @return 列表
     */
    private List<Integer> getStatusValueList(String value) {
        if (value.contains(",")) {
            return Arrays.stream(value.split(",")).map(Integer::parseInt).collect(Collectors.toList());
        } else {
            return Stream.of(Integer.parseInt(value)).collect(Collectors.toList());
        }
    }

    /**
     * 检查ExclusiveGateway Outgoing连接线的ConditionExpression
     *
     * @param expression 条件表达式
     * @return 错误信息
     */
    private String checkConditionExpression(String expression) {
        if (!StringUtils.hasText(expression) || !expression.contains("==")) {
            return "跳转条件表达式未定义";
        }

        String pattern = "#\\{agree==\\d+}";

        if (!expression.replaceAll(" ", "").matches(pattern)) {
            return "跳转条件表达式格式错误，必须为 ‘#{agree == int}’";
        }

        return "";
    }

    private Integer getConditionExpressionValue(String expression) {
        if (!StringUtils.hasText(expression) || !expression.contains("==")) {
            return -1;
        }

        String value = expression.replaceAll(" ", "").replaceAll("#", "").replaceAll("\\{", "")
                .replaceAll("agree", "").replaceAll("==", "").replaceAll("}", "");

        return Integer.parseInt(value);
    }

    /**
     * 获取UserTask的Extension的定义，不查找desc定义
     *
     * @param userTask UserTask
     * @return 属性Map对象
     */
    private Map<String, String> findUserTaskProperty(UserTask userTask) {
        Map<String, String> map = new HashMap<>();

        ExtensionElements extensionElements = userTask.getExtensionElements();
        CamundaProperties camundaProperties = extensionElements.getElementsQuery().filterByType(CamundaProperties.class).singleResult();

        boolean foundStatus = false;
        FlowNode node = CollectionUtils.isEmpty(userTask.getSucceedingNodes().list()) ? null : userTask.getSucceedingNodes().list().get(0);
        if (node != null && "exclusiveGateway".equals(node.getElementType().getTypeName())) {
            List<String> statusList = node.getOutgoing().stream().map(sf -> getPropertyValue(sf.getExtensionElements(), ModelConstant.STATUS_PROPERTY_NAME)).collect(Collectors.toList());
            map.put(ModelConstant.STATUS_PROPERTY_NAME, String.join(",", statusList));
            foundStatus = true;
        }

        for (CamundaProperty camundaProperty : camundaProperties.getCamundaProperties()) {
            if (camundaProperty.getCamundaName().trim().toLowerCase().equals(ModelConstant.STEP_PROPERTY_NAME)) {
                map.put(ModelConstant.STEP_PROPERTY_NAME, camundaProperty.getCamundaValue());
                continue;
            }

            if (camundaProperty.getCamundaName().trim().toLowerCase().equals(ModelConstant.STATUS_PROPERTY_NAME)) {
                if (!foundStatus) {
                    map.put(ModelConstant.STATUS_PROPERTY_NAME, camundaProperty.getCamundaValue());
                }
                continue;
            }

            map.put(camundaProperty.getCamundaName(), camundaProperty.getCamundaValue());
        }

        return map;
    }

    /**
     * 获取UserTask的Extension的定义，不查找desc定义
     *
     * @param userTask UserTask
     * @return 属性Map对象
     */
    private List<TaskPropertyBO> getUserTaskProperty(UserTask userTask) {
        if (userTask == null) {
            return new ArrayList<>();
        }

        List<TaskPropertyBO> taskPropertyBOList = new ArrayList<>();

        //如果有网关
        FlowNode node = userTask.getSucceedingNodes().singleResult();
        if (node != null && "exclusiveGateway".equals(node.getElementType().getTypeName())) {
            for (SequenceFlow sequenceFlow : node.getOutgoing()) {
                Map<String, String> propertyMap = getPropertyMap(sequenceFlow.getExtensionElements());

                TaskPropertyBO propertyBO = new TaskPropertyBO();
                propertyBO.setStatus(getIntValueThenRemoveKey(propertyMap, ModelConstant.STATUS_PROPERTY_NAME, -1));
                propertyBO.setDesc(getStrValueThenRemoveKey(propertyMap, ModelConstant.STATUS_DESCRIPTION_PROPERTY_NAME));
                propertyBO.setAgree(getConditionExpressionValue(sequenceFlow.getConditionExpression().getDomElement().getTextContent()));
                propertyBO.setOther(propertyMap);
                taskPropertyBOList.add(propertyBO);
            }
        }

        Map<String, String> userTaskPropertyMap = getPropertyMap(userTask.getExtensionElements());
        //如果为空，说明没有网关
        if (CollectionUtils.isEmpty(taskPropertyBOList)) {
            TaskPropertyBO propertyBO = new TaskPropertyBO();
            propertyBO.setTaskName(userTask.getName());
            propertyBO.setTaskStep(getIntValueThenRemoveKey(userTaskPropertyMap, ModelConstant.STEP_PROPERTY_NAME, -1));
            //构造函数中设置了Agree的默认值
            propertyBO.setStatus(getIntValueThenRemoveKey(userTaskPropertyMap, ModelConstant.STATUS_PROPERTY_NAME, -1));
            propertyBO.setDesc(getStrValueThenRemoveKey(userTaskPropertyMap, ModelConstant.STATUS_DESCRIPTION_PROPERTY_NAME));
            propertyBO.setUrl(getStrValueThenRemoveKey(userTaskPropertyMap, ModelConstant.INTERNAL_PROCESS_URL_PROPERTY_NAME));
            propertyBO.setRoleType(getIntValueThenRemoveKey(userTaskPropertyMap, ModelConstant.EXTERNAL_PROCESS_ROLE_TYPE_PROPERTY_NAME, ExternalTaskRoleTypeEnum.DEFAULT.getCode()));
            propertyBO.setOther(userTaskPropertyMap);
            taskPropertyBOList.add(propertyBO);
        } else {
            Integer taskStep = getIntValueThenRemoveKey(userTaskPropertyMap, ModelConstant.STEP_PROPERTY_NAME, -1);
            String url = getStrValueThenRemoveKey(userTaskPropertyMap, ModelConstant.INTERNAL_PROCESS_URL_PROPERTY_NAME);
            Integer roleType = getIntValueThenRemoveKey(userTaskPropertyMap, ModelConstant.EXTERNAL_PROCESS_ROLE_TYPE_PROPERTY_NAME, ExternalTaskRoleTypeEnum.DEFAULT.getCode());
            taskPropertyBOList.forEach(propertyBO -> {
                propertyBO.setTaskName(userTask.getName());
                propertyBO.setTaskStep(taskStep);
                propertyBO.setUrl(url);
                propertyBO.setRoleType(roleType);
                propertyBO.getOther().putAll(userTaskPropertyMap);
            });
        }

        return taskPropertyBOList;
    }

    /**
     * 校验属性Map对象的值
     *
     * @param map              属性Map对象
     * @param mustDefinedNameList 必须定义的属性名称列表
     * @return 错误信息
     */
    private String checkPropertyMap(Map<String, String> map, List<String> mustDefinedNameList) {
        return checkPropertyMap(map, mustDefinedNameList, new ArrayList<>());
    }

    /**
     * 校验属性Map对象的值
     *
     * @param map              属性Map对象
     * @param mustDefinedNameList 必须定义的属性名称列表
     * @param optionalDefinedNameList 可选定义的属性名称列表
     * @return 错误信息
     */
    private String checkPropertyMap(Map<String, String> map, List<String> mustDefinedNameList, List<String> optionalDefinedNameList) {
        if (map == null || map.isEmpty()) {
            return "Extension属性为空";
        }

        List<String> notExistNames = mustDefinedNameList.stream().filter(propertyName -> map.entrySet().stream().noneMatch(entry -> entry.getKey().equals(propertyName))).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(notExistNames)) {
            return "未在Extensions中定义" + String.join(",", notExistNames) + "属性";
        }

        for (String s : mustDefinedNameList) {
            if(s.equals(ModelConstant.STEP_PROPERTY_NAME)) {
                String value = map.get(ModelConstant.STEP_PROPERTY_NAME);
                try {
                    int i = Integer.parseInt(value);
                } catch (Exception e) {
                    return ModelConstant.STEP_PROPERTY_NAME + "属性值必须为整数";
                }
                continue;
            }

            if(s.equals(ModelConstant.STATUS_PROPERTY_NAME)) {
                String value = map.get(ModelConstant.STATUS_PROPERTY_NAME);
                if (value.contains(",")) {
                    String[] tmp = value.split(",");
                    for (String s1 : tmp) {
                        try {
                            int j = Integer.parseInt(s1);
                        } catch (Exception e) {
                            return ModelConstant.STATUS_PROPERTY_NAME + "属性值必须为整数";
                        }
                    }
                } else {
                    try {
                        int i = Integer.parseInt(value);
                    } catch (Exception e) {
                        return ModelConstant.STATUS_PROPERTY_NAME + "属性值必须为整数";
                    }
                }
            }
        }

        for (String s : optionalDefinedNameList) {
            if(s.equals(ModelConstant.EXTERNAL_PROCESS_ROLE_TYPE_PROPERTY_NAME) && map.containsKey(ModelConstant.EXTERNAL_PROCESS_ROLE_TYPE_PROPERTY_NAME)) {
                String value = map.get(ModelConstant.EXTERNAL_PROCESS_ROLE_TYPE_PROPERTY_NAME);
                try {
                    if(!ExternalTaskRoleTypeEnum.toList().contains(Integer.parseInt(value))) {
                        return ModelConstant.EXTERNAL_PROCESS_ROLE_TYPE_PROPERTY_NAME + "属性值必须定义为" + ExternalTaskRoleTypeEnum.toList().stream().map(String::valueOf).collect(Collectors.joining("或"));
                    }
                } catch (Exception e) {
                    return ModelConstant.EXTERNAL_PROCESS_ROLE_TYPE_PROPERTY_NAME + "属性值必须为大于0的整数";
                }
            }
        }

        return "";
    }

    /**
     * 从节点的Extensions中获取自定义的属性
     *
     * @param extensionElements 节点的Extensions属性
     * @return 属性Map
     */
    private Map<String, String> getPropertyMap(ExtensionElements extensionElements) {
        Map<String, String> map = new HashMap<>();
        if (extensionElements == null) {
            return map;
        }

        CamundaProperties camundaProperties = extensionElements.getElementsQuery().filterByType(CamundaProperties.class).singleResult();
        camundaProperties.getCamundaProperties().forEach(p -> {
            String key = p.getCamundaName().toLowerCase();
            if (ModelConstant.PROPERTY_NAME_LIST.contains(key)) {
                if (!map.containsKey(key)) {
                    map.put(key, p.getCamundaValue());
                }
            } else {
                if (!map.containsKey(p.getCamundaName())) {
                    map.put(p.getCamundaName(), p.getCamundaValue());
                }
            }
        });

        return map;
    }

    /**
     * 从Extensions中获取自定义的属性值
     *
     * @param extensionElements 节点的Extensions
     * @param propertyName      属性名称
     * @return 属性值
     */
    private String getPropertyValue(ExtensionElements extensionElements, String propertyName) {
        CamundaProperties camundaProperties = extensionElements.getElementsQuery().filterByType(CamundaProperties.class).singleResult();
        CamundaProperty property = camundaProperties.getCamundaProperties().stream().filter(p -> p.getCamundaName().trim().toLowerCase().equals(propertyName)).findFirst().orElse(null);
        return property == null ? "" : property.getCamundaValue();
    }

    /**
     * 从Map中获得字符串类型的属性值，忽略Key的大小写
     *
     * @param propertyMap  属性map
     * @param propertyName 小写的Key
     * @return map的value
     */
    private String getStrValueThenRemoveKey(Map<String, String> propertyMap, String propertyName) {
        String key = propertyMap.containsKey(propertyName) ? propertyName : propertyMap.keySet().stream().filter(k -> k.toLowerCase().equals(propertyName)).findFirst().orElse(null);
        String value = "";


        if (key != null && !"".equals(key)) {
            value = propertyMap.get(key);
            propertyMap.remove(key);
        }

        return value;
    }

    /**
     * 从Map中获得Integer类型的属性值，忽略Key的大小写
     *
     * @param propertyMap  属性map
     * @param propertyName 小写的Key
     * @param defaultValue 如果Map中没有，返回此默认值
     * @return map的value
     */
    private Integer getIntValueThenRemoveKey(Map<String, String> propertyMap, String propertyName, Integer defaultValue) {
        String key = propertyMap.containsKey(propertyName) ? propertyName : propertyMap.keySet().stream().filter(k -> k.toLowerCase().equals(propertyName)).findFirst().orElse(null);
        String value = "";

        if (key != null && !"".equals(key)) {
            value = propertyMap.get(key);
            propertyMap.remove(key);
        }

        return StringUtils.hasLength(value) ? Integer.parseInt(value) : defaultValue;
    }

    /**
     * 将属性Map转换为List<KeyValueBO>
     *
     * @param propertyMap 属性Map
     * @return 转换结果
     */
    private List<KeyValueBO> convertMapToKeyValueList(Map<String, String> propertyMap) {
        if (propertyMap == null || propertyMap.isEmpty()) {
            return new ArrayList<>();
        }

        return propertyMap.entrySet().stream().map(entry -> {
            KeyValueBO bo = new KeyValueBO();
            bo.setKey(entry.getKey());
            bo.setValue(entry.getValue());
            return bo;
        }).collect(Collectors.toList());
    }

    /**
     * 检查Extensions属性定义是否符合要求
     *
     * @param extensionElements 节点的Extensions
     * @param propertyNameList  要检查的属性名称列表
     * @return 错误信息
     */
    private String checkExtensionProperty(ExtensionElements extensionElements, List<String> propertyNameList) {
        if (extensionElements == null) {
            return "未定义Extension属性";
        }

        CamundaProperties pp = extensionElements.getElementsQuery().filterByType(CamundaProperties.class).singleResult();
        List<String> camundaPropertiesNames = pp.getCamundaProperties().stream().map(p -> p.getCamundaName().trim().toLowerCase()).collect(Collectors.toList());


        List<String> finalCamundaPropertiesNames = camundaPropertiesNames;
        List<String> notFoundNames = propertyNameList.stream().filter(name -> !finalCamundaPropertiesNames.contains(name)).collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(notFoundNames)) {
            return "未在Extension中定义" + String.join(",", notFoundNames) + "属性";
        }

        camundaPropertiesNames = pp.getCamundaProperties().stream().filter(p -> !StringUtils.hasText(p.getCamundaValue())).map(p -> p.getCamundaName().trim().toLowerCase()).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(camundaPropertiesNames)) {
            return "Extension属性 " + String.join(",", camundaPropertiesNames) + " 的值为空";
        }

        return "";
    }
}
